import re
import pytz
import datetime
import simplejson
from copy import deepcopy
from typing import List

from latex2sympy2 import latex2sympy


class PipLine:
    """流水线作业"""

    def __init__(self, sample: dict, processes: List[dict]):
        self.sample = sample
        self.raw = deepcopy(sample)
        self.processes = processes
        self.metas = {}
        self.date_format = '%Y-%m-%dT%H:%M:%S.%fZ'  # 默认转换时间展示格式

    def run(self):
        for idx, process in enumerate(self.processes):
            try:
                processor = processor_mapping[process['name']](pipline=self, **process)
                processor.run()
            except KeyError as e:
                self.metas = {'success': False, 'reason': e.args[0], 'idx': idx}
                break
            except Exception as e:
                self.metas = {'success': False, 'idx': idx, 'reason': str(e)}
                break


class Processor:

    PROCESSOR_NAME = ''

    def __init__(self, pipline: PipLine = None, convert_condition: List[dict] = None, **kwargs):
        self.pipline = pipline
        self.convert_condition = convert_condition
        for k, v in kwargs.items():
            setattr(self, k, v)

    def pre_convert(self):
        if self.convert_condition:
            pass
        return True

    def translate(self):
        return {}

    def run(self):
        self.pre_convert()
        return self.translate()

    @staticmethod
    def pytz(name):
        try:
            return pytz.timezone(name)
        except pytz.exceptions.UnknownTimeZoneError:
            raise ValueError('无法识别的时区名称')

    def tz_date(self, date, tz):
        """时间 修正为对应时区的时间"""
        date = date.replace(tzinfo=self.pytz(tz))
        utc_offset = int(date.utcoffset().total_seconds() / 60 / 60) * 60 * 60
        date = date - datetime.timedelta(seconds=utc_offset)
        return date


class FieldProcessor(Processor):
    """字段 操作"""
    pass


class ValueProcessor(Processor):
    """值 操作"""
    pass


class InsertFieldProcessor(FieldProcessor):

    PROCESSOR_NAME = 'InsertField'

    def __init__(self, field, value, **kwargs):
        self.field = field
        self.value = value
        super().__init__(**kwargs)

    def translate(self):
        self.pipline.raw[self.field] = self.value


class CopyFieldProcessor(FieldProcessor):

    PROCESSOR_NAME = 'CopyField'

    def __init__(self, field, s_field, **kwargs):
        self.field = field
        self.s_field = s_field
        super().__init__(**kwargs)

    def translate(self):
        self.pipline.raw[self.field] = self.pipline.raw[self.s_field]


class RenameFieldProcessor(FieldProcessor):
    PROCESSOR_NAME = 'RenameField'

    def __init__(self, field, s_field, **kwargs):
        self.field = field
        self.s_field = s_field
        super().__init__(**kwargs)

    def translate(self):
        self.pipline.raw[self.field] = self.pipline.raw.pop(self.s_field)


class RemoveFieldProcessor(FieldProcessor):

    PROCESSOR_NAME = 'RemoveField'

    def __init__(self, field, **kwargs):
        self.field = field
        super().__init__(**kwargs)

    def translate(self):
        self.pipline.raw.pop(self.field, None)


class AutoRegex(ValueProcessor):
    """划词自动提取正则表达式"""
    PROCESSOR_NAME = 'AutoRegex'

    def __init__(self, field, start_size, end_size, f_name, **kwargs):
        self.field = field
        self.start_size = start_size
        self.end_size = end_size
        self.f_name = f_name
        super().__init__(**kwargs)

    def translate(self):
        pass


class KVProcessor(ValueProcessor):
    PROCESSOR_NAME = 'KV'

    def __init__(self, field, f_delimiter, v_delimiter, strip_chars: str = None, **kwargs):
        self.field = field
        self.f_delimiter = f_delimiter
        self.v_delimiter = v_delimiter
        self.strip_chars = strip_chars
        super(KVProcessor, self).__init__(**kwargs)

    def translate(self):
        if self.strip_chars:
            self.pipline.raw[self.field] = self.pipline.raw[self.field].strip(self.strip_chars)

        for kv in self.pipline.raw[self.field].split(self.f_delimiter):
            k, v = kv.split(self.v_delimiter, maxsplit=1)
            self.pipline.raw[k] = v


class JsonProcessor(ValueProcessor):

    PROCESSOR_NAME = 'JSON'

    def __init__(self, field: str, delimiter: str = None, k_mapping: dict = None, **kwargs):
        self.field = field
        self.delimiter = delimiter
        self.k_mapping = k_mapping
        super().__init__(**kwargs)

    def _flat_by_delimiter(self):
        """扁平化"""
        pass

    def translate(self):
        data = simplejson.loads(self.pipline.raw[self.field])
        if self.delimiter:
            pass

        if self.k_mapping:
            for k, v in self.k_mapping:
                if k in data:
                    data[v] = data.pop(k)
        self.pipline.raw[self.field] = data


class RegexProcessor(ValueProcessor):

    PROCESSOR_NAME = 'Regex'

    def __init__(self, field, regex, **kwargs):
        self.field = field
        self.regex = regex
        super().__init__(**kwargs)

    def translate(self):
        pattern = re.search(self.regex, self.pipline.raw[self.field])
        if not pattern:
            raise KeyError('')

        kv = pattern.groupdict()
        for k, v in kv.items():
            self.pipline.raw[k] = v


class RegexReplaceProcessor(ValueProcessor):

    PROCESSOR_NAME = 'RegexReplace'

    def __init__(self, field, regex, replace, **kwargs):
        self.field = field
        self.regex = regex
        self.replace = replace
        super().__init__(**kwargs)

    def translate(self):
        self.pipline.raw[self.field] = re.sub(self.regex, self.replace, self.pipline.raw[self.field])


class TimeProcessor(ValueProcessor):

    PROCESSOR_NAME = 'Time'

    def __init__(self, field, format, tz='Asia/Shanghai', **kwargs):
        self.field = field
        self.format = format
        self.tz = tz
        super().__init__(**kwargs)

    def translate(self):
        date = datetime.datetime.strptime(self.pipline.raw[self.field], self.format)
        date = self.tz_date(date, self.tz)
        self.pipline.raw[self.field] = date.strftime(self.pipline.date_format)


class TimeStampProcessor(ValueProcessor):

    PROCESSOR_NAME = 'Timestamp'

    def __init__(self, field, tz='Asia/Shanghai', **kwargs):
        self.field = field
        self.tz = tz
        super().__init__(**kwargs)

    def translate(self):
        ts = float(self.pipline.raw[self.field])
        if ts > 15766344150000:  # 微秒
            ts = ts / 1000000
        elif ts > 15766344150:  # 毫秒
            ts = ts / 1000

        date = datetime.datetime.fromtimestamp(float(ts))
        date = self.tz_date(date, self.tz)
        self.pipline.raw[self.field] = date.strftime(self.pipline.date_format)


class LowerProcessor(ValueProcessor):

    PROCESSOR_NAME = 'Lower'

    def __init__(self, field, **kwargs):
        self.field = field
        super().__init__(**kwargs)

    def translate(self):
        self.pipline.raw[self.field] = str(self.pipline.raw[self.field]).lower()


class UpperProcessor(ValueProcessor):

    PROCESSOR_NAME = 'Upper'

    def __init__(self, field, **kwargs):
        self.field = field
        super().__init__(**kwargs)

    def translate(self):
        self.pipline.raw[self.field] = str(self.pipline.raw[self.field]).upper()


class StripProcessor(ValueProcessor):

    PROCESSOR_NAME = 'Strip'

    def __init__(self, field, chars: str = None, **kwargs):
        self.field = field
        self.chars = chars
        super().__init__(**kwargs)

    def translate(self):
        self.pipline.raw[self.field] = str(self.pipline.raw[self.field]).strip(self.chars)


class ConcatProcessor(ValueProcessor):

    PROCESSOR_NAME = 'Concat'

    def __init__(self, field, s_fields: List[str], char: str = '', **kwargs):
        self.field = field
        self.s_fields = s_fields
        self.char = char
        super().__init__(**kwargs)

    def translate(self):
        self.pipline.raw[self.field] = self.char.join([str(self.pipline.raw[s_field]) for s_field in self.s_fields])


class ValueMapProcessor(ValueProcessor):

    PROCESSOR_NAME = 'ValueMap'

    def __init__(self, field, source: dict, default: str = None, **kwargs):
        self.field = field
        self.default = default
        self.source = source
        super().__init__(**kwargs)

    def _v_map(self):
        if self.source['type'] == 'custom':
            return self.source['map']
        return {}

    def translate(self):
        v_map = self._v_map()
        value = self.pipline.raw[self.field]

        if value in v_map:
            self.pipline.raw[self.field] = v_map[value]
            return
        if self.default is not None:
            self.pipline.raw[self.field] = self.default


class ValueSetProcessor(ValueProcessor):

    PROCESSOR_NAME = 'ValueSet'

    def __init__(self, field, value: str, **kwargs):
        self.field = field
        self.value = value
        super().__init__(**kwargs)

    def translate(self):
        self.pipline.raw[self.field] = self.value


class ValueReplaceProcessor(ValueProcessor):

    PROCESSOR_NAME = 'ValueReplace'

    def __init__(self, field, origin: str, replace: str, **kwargs):
        self.field = field
        self.origin = origin
        self.replace = replace
        super().__init__(**kwargs)

    def translate(self):
        self.pipline.raw[self.field] = self.pipline.raw[self.field].replace(self.origin, self.replace)


class ValueMathProcessor(ValueProcessor):

    PROCESSOR_NAME = 'ValueMath'

    def __init__(self, field, formula, s_fields: List, precision: int = None, **kwargs):
        self.field = field
        self.formula = formula
        self.s_fields = s_fields
        self.precision = precision
        super().__init__(**kwargs)

    def translate(self):
        try:
            formula = latex2sympy(self.formula)
        except Exception as e:
            raise Exception('检查公式是否正确')
        symbols = list(formula.free_symbols)
        symbols.sort(key=lambda x: str(x))

        variables = [float(self.pipline.raw[s_field]) for s_field in self.s_fields]
        assert len(symbols) == len(variables), '参数和变量不匹配'
        value = formula.subs(zip(symbols, variables))
        self.pipline.raw[self.field] = round(float(value), self.precision)


field_processor = {sub.PROCESSOR_NAME: sub for sub in FieldProcessor.__subclasses__()}
value_processor = {sub.PROCESSOR_NAME: sub for sub in ValueProcessor.__subclasses__()}
processor_mapping = {**field_processor, **value_processor}


if __name__ == '__main__':
    kv_data = {'log': 'a=b,c=e,d=f,g=,pp=ffa=1,gg={"aa": "bb"},h=33,hh=44,time=2024-01-01 12:00:01,ts=1733712748.588853'}

    pipline_instance = PipLine(kv_data,
                               [
                                   {'name': 'KV', 'field': 'log', 'f_delimiter': ',', 'v_delimiter': '='},
                                   {'name': 'JSON', 'field': 'gg'},

                                   {'name': 'InsertField', 'field': 'insert', 'value': 'data'},
                                   {'name': 'CopyField', 'field': 'copy', 's_field': 'insert'},
                                   {'name': 'RemoveField', 'field': 'insert'},
                                   {'name': 'RenameField', 'field': 'rename', 's_field': 'copy'},

                                   {'name': 'Lower', 'field': 'c'},
                                   {'name': 'Upper', 'field': 'a'},
                                   {'name': 'Strip', 'field': 'pp', 'chars': '1'},
                                   {'name': 'Regex', 'field': 'pp', 'regex': r'(?P<tt>\w+)='},
                                   {'name': 'RegexReplace', 'field': 'c', 'regex': r'[a-z]+', 'replace': '123'},
                                   {'name': 'Concat', 'field': 'concat', 's_fields': ['a', 'c']},

                                   {'name': 'Time', 'field': 'time', 'format': '%Y-%m-%d %H:%M:%S',
                                    'tz': 'Asia/Shanghai'},
                                   {'name': 'Timestamp', 'field': 'ts', 'tz': 'Asia/Shanghai'},

                                   {'name': 'ValueMap', 'field': 'a',
                                    'source': {'type': 'custom', 'map': {"b": '测试'}}},
                                   {'name': 'ValueSet', 'field': 'd', 'value': 'sss'},
                                   {'name': 'ValueReplace', 'field': 'd', 'origin': 'ss', 'replace': 'ccc'},

                                   {'name': 'ValueMath', 'field': 'h', 'formula': 'a+3+b',
                                    's_fields': ['h', 'hh'],
                                    'precision': 3},  # latext 数学公式支持
                               ])
    pipline_instance.run()
    print(pipline_instance.raw, pipline_instance.metas)
